<?php
namespace WeChatPay\Request;

use function WeChatPay\FilterParam;
use function WeChatPay\FuncReturn;

/**
 * JSAPI下单
 * 商户系统先调用该接口在微信支付服务后台生成预支付交易单，返回正确的预支付交易会话标识后再按Native、JSAPI、APP等不同场景生成交易串调起支付。
 * Class WeChatPayTradeJsApiPayRequest
 * @package WeChatPay\Request
 */
class WeChatPayTradeJsApiPayRequest extends AbstractBaseRequest
{
    protected $apiUri = "v3/pay/transactions/jsapi";

    /**
     * 必填参数列表
     * @var array
     */
    protected $mustAddParams = ['description','out_trade_no','amount','payer'];

    /**
     * 属性映射方法名称
     * @var array
     */
    protected $propertyMapSetFunction = [
        'description'   =>  'setDescription',
        'out_trade_no'  =>  'setOutTradeNo',
        'time_expire'   =>  'setTimeExpire',
        'attach'        =>  'setAttach',
        'goods_tag'     =>  'setGoodsTag',
        'amount'        =>  'setAmount',
        'detail'        =>  'setDetail',
        'scene_info'    =>  'setSceneInfo',
        'settle_info'   =>  'setSettleInfo',
        'payer'         =>  'setPayer'
    ];

    /**
     * 商品描述
     * @var string
     */
    protected $description = "";

    /**
     * 商户订单号:商户系统内部订单号，只能是数字、大小写字母_-*且在同一个商户号下唯一
     * @var string
     */
    protected $out_trade_no = "";

    /**
     * 交易结束时间:订单失效时间，遵循rfc3339标准格式，格式为yyyy-MM-DDTHH:mm:ss+TIMEZONE，yyyy-MM-DD表示年月日，T出现在字符串中，表示time元素的开头，HH:mm:ss表示时分秒，TIMEZONE表示时区（+08:00表示东八区时间，领先UTC8小时，即北京时间）。例如：2015-05-20T13:29:35+08:00表示，北京时间2015年5月20日 13点29分35秒。
    订单失效时间是针对订单号而言的，由于在请求支付的时候有一个必传参数prepay_id只有两小时的有效期，所以在重入时间超过2小时的时候需要重新请求下单接口获取新的prepay_id。其他详见时间规则 。
    建议：最短失效时间间隔大于1分钟
     * @var string
     */
    protected $time_expire = "";

    /**
     * 附件数据:在查询API和支付通知中原样返回，可作为自定义参数使用，实际情况下只有支付完成状态才会返回该字段。
     * @var string
     */
    protected $attach = "";

    /**
     * 订单优惠标记
     * @var string
     */
    protected $goods_tag = "";

    /**
     * 订单金额
     * @var array
     */
    protected $amount = [
        //int|must|订单总金额，单位为分
        'total' =>  0,
        //string|货币类型，CNY:人民币
        'currency'  =>  'CNY'
    ];

    /**
     * 优惠功能
     * @var array
     */
    protected $detail = [
        //int
        //1、商户侧一张小票订单可能被分多次支付，订单原价用于记录整张小票的交易金额。
        //2、当订单原价与支付金额不相等，则不享受优惠。
        //3、该字段主要用于防止同一张小票分多次支付，以享受多次优惠的情况，正常支付订单不必上传此参数。
        'cost_price'    =>  10,
        //string|商家小票ID
        'invoice_id'    =>  '12345',
        //list|单品列表信息
        'goods_detail'  =>  [
            [
            //string|must|由半角的大小写字母、数字、中划线、下划线中的一种或几种组成。
            'merchant_goods_id' =>  '46521864123541',
            //string|微信支付定义的统一商品编号（没有可不传）
            'wechatpay_goods_id'    =>  '1234',
            //商品的实际名称
            'goods_name'        =>  '测试商品',
            //int|must|用户购买的数量
            'quantity'          =>  1,
            //int|must|商品单价，单位为分
            'unit_price'        =>  10
            ]
        ]
    ];

    /**
     * 场景信息
     * @var array
     */
    protected $scene_info = [
        //string|must|用户的客户端IP，支持IPv4和IPv6两种格式的IP地址。
        'payer_client_ip'   =>  '127.0.0.1',
        //string|商户端设备号
        'device_id'         =>  '024548478354',
        //object|商户门店信息
        'store_info'        =>  [
            //string|must|商户侧门店编号
            'id'    =>  '0001',
            //string|商户侧门店名称
            'name'  =>  '商户侧门店名称',
            //string|地区编码
            'area_code' => "400700",
            //string|详细的商户门店地址
            'address'   =>  '重庆市'
        ]
    ];

    /**
     *  结算信息
     * @var array
     */
    protected $settle_info = [
        //boolean|是否指定分账
        'profit_sharing'    =>  false
    ];

    /**
     * 支付者
     * @var array
     */
    protected $payer = [
        //string|must|用户标识
        'openid'    =>  ''
    ];

    /**
     * 获取配置基础参数列表(当前接口用到的基础配置中的参数列表)
     * @return array
     */
    public function getBaseParams(): array {
        return ['mchid','appid','notify_url'];
    }

    /**
     * 获取商品描述
     * @return string
     */
    public function getDescription(): string
    {
        return $this->description;
    }

    /**
     * 设置商品描述
     * @param string $description
     */
    public function setDescription(string $description): void
    {
        $this->addBodyParams('description');
        $this->description = $description;
    }

    /**
     * 获取商户订单号
     * @return string
     */
    public function getOutTradeNo(): string
    {
        return $this->out_trade_no;
    }

    /**
     * 设置商户订单号
     * @param string $out_trade_no
     */
    public function setOutTradeNo(string $out_trade_no): void
    {
        $this->addBodyParams('out_trade_no');
        $this->out_trade_no = $out_trade_no;
    }

    /**
     * 获取交易结束时间
     * @return string
     */
    public function getTimeExpire(): string
    {
        return $this->time_expire;
    }

    /**
     * 设置交易结束时间
     * @param string $time_expire
     */
    public function setTimeExpire(string $time_expire): void
    {
        $this->addBodyParams('time_expire');
        $this->time_expire = $time_expire;
    }

    /**
     * 获取附加数据
     * @return string
     */
    public function getAttach(): string
    {
        return $this->attach;
    }

    /**
     * 设置附加数据
     * @param string $attach
     */
    public function setAttach(string $attach): void
    {
        $this->addBodyParams('attach');
        $this->attach = $attach;
    }

    /**
     * 获取订单优惠标记
     * @return string
     */
    public function getGoodsTag(): string
    {
        return $this->goods_tag;
    }

    /**
     * 设置订单优惠标记
     * @param string $goods_tag
     */
    public function setGoodsTag(string $goods_tag): void
    {
        $this->addBodyParams('goods_tag');
        $this->goods_tag = $goods_tag;
    }

    /**
     * 获取订单金额
     * @return array
     */
    public function getAmount(): array
    {
        return $this->amount;
    }

    /**
     * 设置订单金额
     * @param array $amount
     * @throws \Exception
     */
    public function setAmount(array $amount): void
    {
        $this->addBodyParams('amount');
        if (!array_key_exists('total', $amount)) {
            throw new \Exception("amount.total param is must");
        }

        $tempAmount = [
            'total' =>  $amount['total'],
            'currency'  =>  $amount['currency'] ?? 'CNY'
        ];
        $this->amount = $tempAmount;
    }

    /**
     * 获取优惠功能
     * @return array
     */
    public function getDetail(): array
    {
        return $this->detail;
    }

    /**
     * 设置优惠功能
     * @param array $detail
     * @throws \Exception
     */
    public function setDetail(array $detail): void
    {
        $this->addBodyParams('detail');
        $goodDetail = [];
        if (array_key_exists('goods_detail', $detail)) {
            if (!is_array($detail)) {
                throw new \Exception("detail.goods_detail param must is array list");
            }

            foreach ($detail as $key => $tempArr) {
                if (!is_array($tempArr)) {
                    throw new \Exception("detail.goods_detail param must is array list");
                }

                if (!array_key_exists('merchant_goods_id', $tempArr)) {
                    throw new \Exception("detail.goods_detail[{$key}].merchant_goods_id param is must");
                }

                if (!array_key_exists('quantity', $tempArr)) {
                    throw new \Exception("detail.goods_detail[{$key}].quantity param is must");
                }

                if (!array_key_exists('unit_price', $tempArr)) {
                    throw new \Exception("detail.goods_detail[{$key}].unit_price param is must");
                }

                $goodDetail[$key] = [
                    'merchant_goods_id' =>  $tempArr['merchant_goods_id'],
                    'quantity'          =>  $tempArr['quantity'],
                    'unit_price'        =>  $tempArr['unit_price'],
                    'wechatpay_goods_id'=>  $tempArr['wechatpay_goods_id'] ?? "",
                    'goods_name'        =>  $tempArr['goods_name'] ?? ""
                ];
            }
        }


        $tempDetail = [
            'cost_price'    =>  $detail['cost_price'] ?? 0,
            'invoice_id'    =>  $detail['invoice_id'] ?? "",
            'goods_detail'  =>  $goodDetail
        ];
        $this->detail = FilterParam($tempDetail);
    }

    /**
     * 获取场景信息
     * @return array
     */
    public function getSceneInfo(): array
    {
        return $this->scene_info;
    }

    /**
     * 设置场景信息
     * @param array $scene_info
     * @throws \Exception
     */
    public function setSceneInfo(array $scene_info): void
    {
        $this->addBodyParams('scene_info');
        if (!array_key_exists('payer_client_ip', $scene_info)) {
            throw new \Exception("detail.scene_info.payer_client_ip param is must");
        }

        $store_info = [];
        if (array_key_exists('store_info', $scene_info)) {
            if (!array_key_exists('id', $scene_info['store_info'])) {
                throw new \Exception("detail.scene_info.store_info.id param is must");
            }

            $store_info['id'] = $scene_info['store_info']['id'];
            $store_info['name'] = $scene_info['store_info']['name'] ?? "";
            $store_info['area_code'] = $scene_info['store_info']['area_code'] ?? "";
            $store_info['address'] = $scene_info['store_info']['address'] ?? "";
        }

        $tempScene = [
            'payer_client_ip'   =>  $scene_info['payer_client_ip'],
            'device_id'         =>  $scene_info['device_id'] ?? "",
            'store_info'        =>  $store_info
        ];
        $this->scene_info = FilterParam($tempScene);
    }

    /**
     * 获取结算信息
     * @return array
     */
    public function getSettleInfo(): array
    {
        return $this->settle_info;
    }

    /**
     * 设置结算信息
     * @param array $settle_info
     */
    public function setSettleInfo(array $settle_info): void
    {
        $this->addBodyParams('settle_info');
        $tempSettleInfo = [
            'profit_sharing'    =>  $settle_info['profit_sharing'] ?? ""
        ];
        $this->settle_info = FilterParam($tempSettleInfo);
    }

    /**
     * 获取支付者信息
     * @return array
     */
    public function getPayer(): array
    {
        return $this->payer;
    }

    /**
     * 设置支付者信息
     * @param array $payer
     */
    public function setPayer(array $payer): void
    {
        $this->addBodyParams('payer');
        if (!array_key_exists('openid', $payer)) {
            throw new \Exception("payer.openid param is must");
        }

        $this->payer = $payer;
    }
}